local U = require "packages/poker-games/utility"
local desc = [[
# 五十K玩法简介
---
一种由4人游玩的扑克牌游戏。使用两副扑克牌。
对家互为队友，目标是赢得尽可能多的分数
---
## 对局
对局中，一名玩家先进行出牌，随后逆时针询问其他玩家是否出牌，出牌方式为比牌型大小。然后基于此次出牌重新询问其他玩家是否出牌。
当所有其他玩家都选择不出牌后，该玩家收走场上本次所出牌的所有分数牌。



什么是分数牌

分数牌指5、10和K，其中5代表5分，10与K代表10分。所有牌分数之和为200分
一名玩家收走分数牌即代表其队伍本局赢得了对应牌分数。所以游戏的目标就是收集分数牌，获得更多分数

---

## 牌型

比大小的关键是比牌型。在五十K中，牌型会被分为两类，即普通牌型与套牌：
普通牌包含基本牌型：单牌、对子、三张、连对、顺子、三代二、飞机
需要注意的是，普通牌型之间不能比较大小，也就是不能用一种普通牌型响应另一种，只能使用相同的普通牌型响应

而普通牌型内部的大小则取决于点数与花色。在13种点数中，3最小，2最大，A次大。剩余点数依照数字排列大小。
而花色中的大小顺序为 方片<梅花<红桃<黑桃 。

在单牌的比较中，优先比较点数，点数相同则比较花色；

在对子比较中，仅仅比较对子的点数大小，三张也是如此；

在连对比较中，优先比较连对的长度（即有多少对），然后比较末尾点数的大小；

在顺子比较中，优先比较顺子的长度，然后比较末尾点数大小；

在三带二（可以任意带）比较中，仅比较三张相同点数牌的点数大小；

在飞机比较中，优先比较连续的三张牌的长度（即组数），长度相同则比较最大的相同三张牌的点数

除开普通牌型外，还有套牌，套牌可以任意响应普通牌，即套牌比普通牌更大。同时套牌之间也有大小差异。
其类型有：五十K、炸弹、同花顺、天炸

五十K：即5、10、K各一张组成的牌型，其中杂色的五十K是最小的套牌，小于相同花色的五十K，即正五十K。

炸弹：由四张或以上相同点数的牌组成。炸弹之间的比较优先看炸弹的长度，其次为点数。

同花顺：由五张或以上的相同花色的顺子组成，同花顺之间的大小判断与顺子相同，在长度与末尾点数相同时，则比较花色。

特殊的，炸弹与同花顺的比较优先看长度，长度相同则同花顺胜。

天炸：由四张鬼牌组成，是最大的套牌。

---

## 结算
游戏由于是两两组队，所以需要相互帮助。当一局游戏中的某一次出牌响应结束后（即无人响应）。若场上由至少三名玩家出完了牌，本局游戏结束，进入计分阶段。
双方根据本轮获得的分数牌计算分数，同时按照出完所有牌的先后排名获得额外加分：

第一名 +60

第二名 +40

第三名 +20

第四名 +0

如果本局游戏结束时，第四名角色仍然有手牌且其中有分数牌，那么其队伍将扣除两倍于未打出分数牌的分数

最后，每个人将自己队伍的分数加到总分上。

**特殊规则**

在一局游戏中，如果同一支队伍的两位玩家位于第三和第四名，则称之为“双下”，那么在下一局开始前，第二名玩家将与第四名玩家交换座位。
同时变更组队。
]]

local test = false

---SetTurn
---@param room Room
---@param starter ServerPlayer
---@return ServerPlayer
local turn = function(room, starter)
  --回合逻辑
  local player = starter
  local winner = player

  for _, p in ipairs(room.players) do
    if p == starter then
      room:setPlayerProperty(p, "phase", Player.Play)
    else
      room:setPlayerProperty(p, "phase", Player.NotActive)
    end
  end

  local suc, dat = room:askToUseActiveSkill(starter, {
    skill_name = "ftk_just_play",
    cancelable = false,
    prompt = "#ftk_just_play-invoke",
    skip = false
  })
  if suc and dat then
    local cids = dat.cards
    local c = Fk:cloneCard(dat.interaction)
    c:addSubcards(cids)
    room:useCard{
      from = starter,
      tos = {},
      card = c
    }
  else --处理人机，让他随机出一张单牌
    local cid = table.random(starter:getCardIds("h"), 1)
    local c = Fk:cloneCard("ftk__single")
    c:addSubcards(cid)
    room:useCard{
      from = starter,
      tos = {},
      card = c
    }
  end

  winner = room:getTag("ftk_current_winner") or player
  local current_cards = room:getBanner("@$ftk_current_cards") or {}
  if current_cards ~= 0 and #current_cards > 0 then
    local hand_scores = 0
    local score_cards = table.filter(current_cards, function (id)
      local c = Fk:getCardById(id)
      if table.contains({5, 10, 13}, c.number) then
        hand_scores = hand_scores + (c.number == 5 and 5 or 10)
        return true
      end
    end)
    room:moveCardTo(score_cards, Card.PlayerSpecial, winner, nil, nil, "#ftkScoreCards")
    local allys = U.getFTKAllys(room, winner)
    for _, p in ipairs(allys) do
      room:addPlayerMark(p, "@FTKteam_score-round", hand_scores)
    end
  end
  room:setBanner("@$ftk_current_cards", {})
  return winner
end

---@param room Room
local round = function(room, winner)
  --游戏局逻辑
  local roundCount = room:getTag("RoundCount") or 0
  roundCount = roundCount + 1
  room:setTag("RoundCount",  roundCount)
  room:doBroadcastNotify("UpdateRoundNum", roundCount)

  --双下换位机制
  local rank = room:getBanner("FTKround_rank") or {}
  if #rank == 4 then
    if table.contains(U.getFTKAllys(room, rank[1]), rank[2]) then
      room:sendLog{
        type = "#FTKSwapSeat"
      }
      room:swapSeat(rank[2], rank[4])
    end
  end
  --清除上一局的排名并重新开始新的一局
  room:setBanner("FTKround_rank", 0)
  local players = table.simpleClone(room.players)
  local team1 = {room.players[1], room.players[3]}
  local team2 = {room.players[2], room.players[4]}
  room:setTag("FTKteams", { ["team1"] = team1, ["team2"] = team2 })
  room:shuffleDrawPile()
  for _, p in ipairs(players) do
    local cids = room:getNCards(test and 1 or 26)
    table.sort(cids, function (a, b)
      local numa, numb = U.checkNumber(Fk:getCardById(a).number), U.checkNumber(Fk:getCardById(b).number)
      if numa > numb then
        return true
      elseif numa == numb then
        return U.checkSuit(Fk:getCardById(a).suit) > U.checkSuit(Fk:getCardById(b).suit)
      end
      return false
    end)
    room:obtainCard(p, cids, false)
  end

  while true do
    local starter = room:getTag("ftk_current_winner") or winner
    while starter:isKongcheng() do
      starter = starter.next
    end
    turn(room, starter)
    if #table.filter(room.players, function (p)
      return #p:getCardIds("h") == 0
    end) >= 3 then
      break
    end
  end

  --补足排名
  if #room:getBanner("FTKround_rank") < 4 then
    local mark = room:getBanner("FTKround_rank")
    for _, p in ipairs(room.players) do
      if not table.contains(room:getBanner("FTKround_rank"), p) then
        table.insert(mark, p)
        room:setPlayerMark(p, "FTKround_rank", #mark)
      end
    end
    room:setBanner("FTKround_rank", mark)
  end

  room:setTag("ftk_current_winner", nil)

  --这里处理单局分数
  room:sendLog{
    type = "#FTKEndRound",
    toast = true
  }
  local rank_score_list = {}
  local card_score_list = {}
  local rank_list = {}
  for k, p in pairs(room:getBanner("FTKround_rank")) do
    local hidden_scores = 0
    if #p:getCardIds("h") ~= 0 then
      for _, id in ipairs(p:getCardIds("h")) do
        local c = Fk:getCardById(id)
        if table.contains({5, 10, 13}, c.number) then
          hidden_scores = hidden_scores + (c.number == 5 and 5 or 10)
        end
      end
      if hidden_scores > 0 then
        room:sendLog{ --惩罚机制
          type = "#FTKScorePunish",
          from = p.id,
          arg = tostring(hidden_scores),
        }
      end
    end
    rank_score_list[p] = 80 - (20 * k) --排位得分
    card_score_list[p] = p:getMark("@FTKteam_score-round") - (2 * hidden_scores)
    rank_list[p] = k
  end
  for team, ps in pairs(room:getTag("FTKteams")) do
    local score = rank_score_list[ps[1]] + rank_score_list[ps[2]] + card_score_list[ps[1]]
    for _, p in ipairs(ps) do
      room:addPlayerMark(p, "@FTK_total_score", score)
      room:sendLog{
      type = "#FTKPersonalScoreSettlement",
      from = p.id,
      arg = tostring(score),
      arg2 = tostring(p:getMark("@FTK_total_score")),
      arg3 = tostring(rank_list[p])
    }
    end
  end

  --延迟一下，便于查看信息
  room:animDelay(test and 1 or 10)

  --在这里先把牌扔了，先不清排名便于双下换位判断
  for _, p in ipairs(room.players) do
    room:setPlayerMark(p, "@FTKteam_score-round", 0)
    room:setPlayerMark(p, "@FTKrank-round", 0)
    p:throwAllCards("h")
    room:throwCard(p:getPile("#ftkScoreCards"), nil, p, p)
  end

  return room:getBanner("FTKround_rank")[1]
end

---@param room Room
local game = function(room)
  --游戏逻辑
  room.current = room.players[1]
  local choice = room:askToChoice(room.current, {
    choices = { "1", "5", "10", "15", "20" },
    skill_name = "",
    prompt = "#ftk-rule-select",
  })
  if room.current.id < 0 then choice = "1" end

  --初始给上100分避免负数，同时也是把标记坑位占住
  for _, p in ipairs(room.players) do
    room:setPlayerMark(p, "@FTK_total_score", 100)
  end

  --这里进入游戏循环
  room:setBanner("@FTKrounds", choice)
  local starter = room.current
  while (room:getTag("RoundCount") or 0) < tonumber(choice) do
    starter = round(room, starter)
  end

  -- 最后结算
  local rank = table.map(room.players, function (p)
    return { p, p:getMark("@FTK_total_score") }
  end)
  table.sort(rank, function (a, b)
    return a[2] > b[2]
  end)
  local winners = { rank[1][1] }
  for i = 2, #rank do
    if rank[i][2] == rank[1][2] then
      table.insert(winners, rank[i][1])
    end
  end
  local arg = rank[1][1]._splayer:getScreenName()
  if #winners > 1 then
    arg = table.concat(table.map(winners, function (p)
      return p._splayer:getScreenName()
    end), " 、")
  end
  room:sendLog{
    type = "#FTKGamesOver",
    arg = arg,
    arg2 = rank[1][2],
    toast = true,
  }

  --潦草处理最后的赢家
  for _, p in ipairs(room.players) do
    if table.contains(winners, p) then
      room:setPlayerProperty(p, "role", "lord")
    else
      room:setPlayerProperty(p, "role", "rebel")
    end
  end

  room:gameOver("lord")
end

local logic = function()
  ---@class FTKLogic: GameLogic
  local FTKLogic = GameLogic:subclass("FTKLogic")
  function FTKLogic:chooseGenerals()
    local room = self.room
    for _, p in ipairs(room.players) do
      local avatar = p._splayer:getAvatar()
      if avatar == "anjiang" then avatar = table.random{ "blank_shibing", "blank_nvshibing" } end
      local avatar_general = Fk.generals[avatar] or Fk.generals["sunce"] or Fk.generals["diaochan"]
      room:setPlayerProperty(p, "general", avatar_general.name)
      room:setPlayerProperty(p, "kingdom", avatar_general.kingdom)
      room:setPlayerProperty(p, "role_shown", true)
    end
  end
  function FTKLogic:broadcastGeneral() end
  function FTKLogic:attachSkillToPlayers() end
  function FTKLogic:prepareForStart()
    local room = self.room
    local add_cards = {}
    for _, id in ipairs(room.draw_pile) do
      local c = Fk:getCardById(id)
      table.insert(add_cards, room:printCard(c.name, c.suit, c.number).id)
    end
    room:moveCardTo(add_cards, Card.DrawPile, nil, nil, nil, nil, false)
    for _, id in ipairs(room.draw_pile) do
      local c = Fk:getCardById(id)
      if c.number == 5 then
        room:setCardMark(c, "@@ftk_socre_5", 5)
      elseif c.number == 10 or c.number == 13 then
        room:setCardMark(c, "@@ftk_socre_10", 10)
      end
    end
    room:shuffleDrawPile()
  end
  function FTKLogic:action()
    self.room:addSkill("ftk_room_trigger")
    game(self.room)
  end
  return FTKLogic
end

local mode = fk.CreateGameMode {
  name = "ftk_games",
  minPlayer = 4,
  maxPlayer = 4,
  logic = logic,
  whitelist = function(self, pkg)
    if pkg.type ~= Package.CardPack then return true end
    return pkg.name == "poker-deck"
  end,
}

Fk:loadTranslationTable {
  ["ftk_games"] = "五十K",
  [":ftk_games"] = desc,
  ["#ftk-rule-select"] = "选择目标局数",

  ["@@ftk_socre_5"] = "5分",
  ["@@ftk_socre_10"] = "10分",
  ["@$ftk_current_cards"] = "当前牌池",
  ["@FTKrounds"] = "目标局数",
  ["#ftkScoreCards"] = "分数牌堆",
  ["@FTKteam_score-round"] = "本局分数",
  ["#FTKEndRound"] = "本局结束，可在战报中查看分数",
  ["@FTK_total_score"] = "<font color='yellow'>总分</font>",
  ["#FTKPersonalScoreSettlement"] = "%from（第 %arg3）获得了 %arg 分，当前为 %arg2 分",
  ["#FTKSwapSeat"] = "由于上一局中出现了同一队双人下游，第二、四名将更换位置",
  ["#FTKScorePunish"] = "%from 手中保留了 %arg 分，他的队伍将收到双倍的分数惩罚！",
  ["#ftk_just_play-invoke"] = "现在是你先手出牌",
  ["#FTKGamesOver"] = "游戏结束，第一名是 %arg ，总分为 %arg2",
}

return mode